package cn.edu.nit.sys.utils;

import cn.edu.nit.sys.entity.*;
import cn.edu.nit.sys.mapper.SysInfoMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import oshi.SystemInfo;
import oshi.hardware.*;
import oshi.software.os.*;
import oshi.util.Util;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.*;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;

/**
 * @Author xiaobai
 * @Date 2022/8/28 22:24
 * @Version 1.0.0
 */
@Component
public class SysRuntimeInfo {

    @Autowired
    private SysInfoMapper sysInfoMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static SystemInfo systemInfo = new SystemInfo();
    private static HardwareAbstractionLayer hardware = systemInfo.getHardware();

    /**
     * CPU 基本信息
     * @return
     */
    public JSONObject getCpuInfo() {
        JSONObject cpuInfo = new JSONObject();
        CentralProcessor processor = hardware.getProcessor();
        CentralProcessor.ProcessorIdentifier processorIdentifier = processor.getProcessorIdentifier();
        //CPU名称
        cpuInfo.put("CPU名称", processorIdentifier.getName());
        //CPU核数
        cpuInfo.put("CPU核数", processor.getPhysicalProcessorCount());
        //CPU线程
        cpuInfo.put("CPU线程", processor.getLogicalProcessorCount());
        long vendorFreq = processorIdentifier.getVendorFreq();
        cpuInfo.put("CPU基本频率", new DecimalFormat("#.00Ghz").format(vendorFreq / Math.pow(10, 9)));
        return cpuInfo;
    }

    /**
     * 60s CPU使用情况
     * @param time
     */
    @Async
    public void methods(Long time) {
        JSONObject cpuRate = new JSONObject();
        CentralProcessor processor = hardware.getProcessor();
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Util.sleep(time);
        long[] ticks = processor.getSystemCpuLoadTicks();
        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        long user = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
//        long[] ticks = processor.getSystemCpuLoadTicks();
//        long nice = ticks[CentralProcessor.TickType.NICE.getIndex()];
//        long irq = ticks[CentralProcessor.TickType.IRQ.getIndex()];
//        long softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
//        long steal = ticks[CentralProcessor.TickType.STEAL.getIndex()];
//        long cSys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()];
//        long user = ticks[CentralProcessor.TickType.USER.getIndex()];
//        long iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()];
//        long idle = ticks[CentralProcessor.TickType.IDLE.getIndex()];
//        long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal;
        //CPU当前使用率
        String rate = new DecimalFormat("#.##%").format(1.0 - (idle * 1.0 / totalCpu));
        cpuRate.put("CPU当前使用率", new DecimalFormat("#.##%").format(1.0 - (idle * 1.0 / totalCpu)));
        SysInfo cpu = new SysInfo();
        cpu.setRate(rate);
        cpu.setTotal(totalCpu);
        cpu.setUsed(totalCpu - idle);
        cpu.setType(1);
        sysInfoMapper.insert(cpu);
    }

    /**
     * 获取内存使用情况
     * @return
     */
    public JSONObject getMemory() {
        JSONObject memoryInfo = new JSONObject();
        GlobalMemory memory = hardware.getMemory();
        long available = memory.getAvailable();
        long total = memory.getTotal();
        memoryInfo.put("total", formatByte(total));
        memoryInfo.put("used", formatByte(total - available));
        memoryInfo.put("useRate", new DecimalFormat("#.##%").format((total - available) * 1.0 / total));
        memoryInfo.put("lTotal", total);
        memoryInfo.put("lUsed", total - available);
        return memoryInfo;
    }

    /**
     * 获取硬盘信息
     * @return
     */
    public List<Hs> getSysFileInfo() {
        JSONObject sysFileInfo = new JSONObject();
        OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
        FileSystem fileSystem = operatingSystem.getFileSystem();
        List<HWDiskStore> diskStores = hardware.getDiskStores();
        List<OSFileStore> fileStores = fileSystem.getFileStores();  // 磁盘信息
        List<Hs> hsList = new ArrayList<>();
        for(HWDiskStore hwDiskStore : diskStores) {
            Hs hs = new Hs();
            List<Hp> hpList = new ArrayList<>();
            hs.setModel(hwDiskStore.getModel());     // 硬盘名称
            hs.setSize(formatByte(hwDiskStore.getSize()));   // 硬盘大小
            List<HWPartition> partitions = hwDiskStore.getPartitions();  // 硬盘分区
            for(HWPartition hwPartition : partitions) {
                Hp hp = new Hp();
                List<Os> osList = new ArrayList<>();
                for(OSFileStore osFileStore : fileStores) {
                    Os os = new Os();
                    if(osFileStore.getMount().equals(hwPartition.getMountPoint())) {
                        //磁盘名称
                        os.setName(osFileStore.getName());
                        //盘符路径
                        os.setDirName(osFileStore.getMount());
                        //文件类型
                        os.setTypeName(osFileStore.getName());
                        //总大小
                        os.setTotal(formatByte(osFileStore.getTotalSpace()));
                        os.setLTotal(osFileStore.getTotalSpace());
                        //剩余大小
                        os.setFree(formatByte(osFileStore.getUsableSpace()));
                        os.setLFree(osFileStore.getFreeSpace());
                        //已经使用量
                        os.setUsed(formatByte(osFileStore.getTotalSpace() - osFileStore.getUsableSpace()));
                        os.setLUsed(osFileStore.getTotalSpace() - osFileStore.getUsableSpace());
                        osList.add(os);
                    }
                }
                hp.setOsList(osList);
                hpList.add(hp);
            }
            hs.setHpList(hpList);
            hsList.add(hs);
        }
        return hsList;
    }

    /**
     * 获取系统信息
     * @return
     */
    public JSONObject getSysInfo() {
        JSONObject sysInfo = new JSONObject();
        CentralProcessor processor = hardware.getProcessor();
        Properties props = System.getProperties();
        // 操作系统名
        sysInfo.put("osName", props.getProperty("os.name"));
        // 系统架构
        sysInfo.put("osArch", props.getProperty("os.arch"));
        // 运行环境
        sysInfo.put("environment", props.getProperty("java.runtime.name"));
        try {
            // 服务器名称
            sysInfo.put("computerName", InetAddress.getLocalHost().getHostName());
            // 服务器IP
            sysInfo.put("computerIp", InetAddress.getLocalHost().getHostAddress());
        } catch (UnknownHostException e) {
           e.printStackTrace();
        }
        //项目路径
        sysInfo.put("userDir", props.getProperty("user.dir"));
        return sysInfo;
    }

    /**
     * 网络收发包情况
     * @param wait
     */
    @Async
    public void netInfoLoop(Long wait) {
        List<NetworkIF> networkIFs = hardware.getNetworkIFs();
        long up = 0;
        long down = 0;
        for (NetworkIF net : networkIFs) {
            up += net.getBytesSent();
            down += net.getBytesRecv();
        }

        Util.sleep(wait);

        networkIFs = hardware.getNetworkIFs();
        long upload = 0;
        long download = 0;
        for (NetworkIF net : networkIFs) {
            upload += net.getBytesSent();
            download += net.getBytesRecv();
        }
        String downloadStr = new DecimalFormat("#.##").format((download - down) / 1000.0 / wait * 1000);
        String uploadStr = new DecimalFormat("#.##").format((upload - up) / 1000.0 / wait * 1000);
        SysInfo sysInfo = new SysInfo();
        sysInfo.setRevc(downloadStr);
        sysInfo.setSend(uploadStr);
        sysInfo.setType(2);
        sysInfoMapper.insert(sysInfo);
    }

    /**
     * JVM信息
     * @return
     */
    RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
    long startTime = bean.getStartTime();
    public JSONObject getJvmInfo() {
        JSONObject jvmInfo = new JSONObject();
        Date date = new Date(startTime);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String format = simpleDateFormat.format(date);
        jvmInfo.put("startTime", format);
        long nowTime = System.currentTimeMillis();
        long duration = nowTime - startTime;
        long ss = (duration % (1000 * 60)) / 1000;                    // 秒
        long mm = (duration % (1000 * 60 * 60)) / (1000 * 60);        // 分钟
        long hh = (duration % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);   // 小时
        String runTime = (hh < 10 ? "0" + hh : hh)
                + "小时" + (mm < 10 ? "0" + mm : mm) + "分"
                + (ss < 10 ? "0" + ss : ss) + "秒";
        jvmInfo.put("runTime", runTime);
        Properties properties = System.getProperties();
        Runtime runtime = Runtime.getRuntime();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long useMemory = totalMemory - freeMemory;
        jvmInfo.put("total", formatByte(totalMemory));
        jvmInfo.put("used", formatByte(useMemory));
        jvmInfo.put("lTotal", totalMemory);
        jvmInfo.put("lUsed", useMemory);
        jvmInfo.put("useRate", new DecimalFormat("#.##%").format(useMemory * 1.0 / totalMemory));
        jvmInfo.put("jdkVersion", properties.getProperty("java.version"));
        jvmInfo.put("jdkPath", properties.getProperty("sun.boot.library.path"));
        jvmInfo.put("jvmName", properties.getProperty("java.vm.name"));
        return jvmInfo;
    }

    /**
     * 获取数据库、Redis版本信息
     */
    private final String url = "jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf-8";
    private final String username = "root";
    private final String password = "12345678";
    public JSONObject getOtherInfo() throws Exception{
        JSONObject otherInfo = new JSONObject();
        Connection connection = DriverManager.getConnection(url, username, password);
        DatabaseMetaData metaData = connection.getMetaData();
        metaData.getDatabaseProductVersion();
        metaData.getDatabaseProductName();
        Properties info = stringRedisTemplate.getConnectionFactory().getConnection().info("server");
        otherInfo.put("version", info.getProperty("redis_version"));
        otherInfo.put("arch", info.getProperty("arch_bits"));
        otherInfo.put("os", info.getProperty("os"));
        otherInfo.put("port", info.getProperty("tcp_port"));
        otherInfo.put("dataName", metaData.getDatabaseProductName());
        otherInfo.put("dataVersion", metaData.getDatabaseProductVersion());
        otherInfo.put("jdbcName", metaData.getDriverName());
        otherInfo.put("jdbcVersion", metaData.getDriverVersion().split("/\\(/g")[0]);
        return otherInfo;
    }

    /**
     * 单位转换
     */
    private static String formatByte(long byteNumber) {
        //换算单位
        double FORMAT = 1024.0;
        double kbNumber = byteNumber / FORMAT;
        if (kbNumber < FORMAT) {
            return new DecimalFormat("#.##KB").format(kbNumber);
        }
        double mbNumber = kbNumber / FORMAT;
        if (mbNumber < FORMAT) {
            return new DecimalFormat("#.##MB").format(mbNumber);
        }
        double gbNumber = mbNumber / FORMAT;
        if (gbNumber < FORMAT) {
            return new DecimalFormat("#.##GB").format(gbNumber);
        }
        double tbNumber = gbNumber / FORMAT;
        return new DecimalFormat("#.##TB").format(tbNumber);
    }
}
